﻿using System;
using System.Collections.Generic;
using System.Runtime.CompilerServices;
using System.Threading;
using System.Threading.Tasks;
using DotNetCommon.Extensions;

namespace DotNetCommon.Validate;

public interface IValidateAction
{
    void Action(IValidateContext ctx);
}

public interface IValidateContext
{
    /// <summary>
    /// 当前节点是否已经终止验证
    /// </summary>
    public bool StopValidate { get; set; }

    /// <summary>
    /// 错误提示信息
    /// </summary>
    public IReadOnlyList<(string path, string error)> ErrorMessage { get; }
    public void AddError(string path, string message);
    /// <summary>
    /// 是否已经产生的错误
    /// </summary>
    public bool HasErrors();

    /// <summary>
    /// 子节点
    /// </summary>
    public List<IValidateContext> Children { get; }

    /// <summary>
    /// 父节点
    /// </summary>
    public IValidateContext Parent { get; }

    /// <summary>
    /// 异步取消token
    /// </summary>
    public CancellationToken CancellationToken { get; }

    /// <summary>
    /// 校验属性名称
    /// </summary>
    public string ModelName { get; }

    /// <summary>
    /// 当前节点路径
    /// </summary>
    public string ModelPath { get; }

    /// <summary>
    /// 当前节点的值, 推荐使用 ctx.Model
    /// </summary>
    public object ModelValue { get; }
}

public interface IValidateContext<out T> : IValidateContext
{
    /// <summary>
    /// 模型
    /// </summary>
    public T Model { get; }

    /// <summary>
    /// todo: 如果验证失败就退出
    /// </summary>
    /// <returns></returns>
    IValidateContext<T> IfFailThenExit();
    /// <summary>
    /// 自定义校验规则, 不抛出异常表示校验成功
    /// </summary>
    IValidateContext<T> MustNotException(Action<T> mustNotException, string errorMessage = null);
    /// <summary>
    /// 自定义校验规则, 不抛出异常表示校验成功
    /// </summary>
    Task MustNotExceptionAsync(Func<T, CancellationToken, Task> mustNotException, string errorMessage = null, CancellationToken? cancellationToken = null);
    /// <summary>
    /// 自定义校验规则, 返回 true 表示校验成功
    /// </summary>
    IValidateContext<T> MustReturnTrue(Func<T, bool> mustReturnTrue, string errorMessage = null);
    /// <summary>
    /// 自定义校验规则, 返回 true 表示校验成功
    /// </summary>
    Task MustReturnTrueAsync(Func<T, CancellationToken, Task<bool>> mustReturnTrue, string errorMessage = null, CancellationToken? cancellationToken = null);
    /// <summary>
    /// 为某个属性开启新的校验
    /// </summary>
    IValidateContext<TProp> RuleFor<TProp>(Func<T, TProp> memberFunc, [CallerArgumentExpression(nameof(memberFunc))] string conditionExpression = null);
    /// <summary>
    /// 为某个属性开启新的校验
    /// </summary>
    IValidateContext<T> RuleForIfNotNull<TProp>(Func<T, TProp> memberFunc, Action<IValidateContext<TProp>> act, [CallerArgumentExpression(nameof(memberFunc))] string conditionExpression = null);
    /// <summary>
    /// 为某个属性开启新的校验
    /// </summary>
    Task RuleForIfNotNullAsync<TProp>(Func<T, TProp> memberFunc, Func<IValidateContext<TProp>, CancellationToken, Task> act, CancellationToken? cancellationToken = null, [CallerArgumentExpression(nameof(memberFunc))] string conditionExpression = null);
    /// <summary>
    /// 使用其他自定义的校验规则
    /// </summary>
    IValidateContext<T> UseValidate(Action<IValidateContext<T>> action);
    /// <summary>
    /// 使用其他自定义的校验规则
    /// </summary>
    Task UseValidateAsync(Func<IValidateContext<T>, CancellationToken, Task> action, CancellationToken? cancellationToken = null);
    /// <summary>
    /// 当满足条件时, 执行校验规则
    /// </summary>
    IValidateContext<T> When(Func<T, bool> predicate, Action<IValidateContext<T>> action);
    /// <summary>
    /// 当不为null时, 执行校验规则
    /// </summary>
    IValidateContext<T> WhenNotNull(Action<IValidateContext<T>> action);
    /// <summary>
    /// 当满足条件时, 执行校验规则
    /// </summary>
    /// <param name="predicate"></param>
    /// <param name="action"></param>
    /// <param name="cancellationToken"></param>
    /// <returns></returns>
    Task WhenAsync(Func<T, CancellationToken, Task<bool>> predicate, Func<IValidateContext<T>, CancellationToken, Task> action, CancellationToken? cancellationToken = null);
    /// <summary>
    /// 设置当前节点的名称
    /// </summary>
    IValidateContext<T> WithDisplayName(string name);
}


internal abstract class ValidateContext : IValidateContext
{
    protected ValidateContext(string modelName, IValidateContext parent, bool stopValidate, CancellationToken cancellationToken)
    {
        ModelName = modelName;
        parent?.Children.Add(this);
        Parent = parent;
        StopValidate = stopValidate;
        CancellationToken = cancellationToken;
        if (parent == null) ErrorMessageInternal = [];
    }
    public bool StopValidate { get; set; }
    public List<(string path, string error)> ErrorMessageInternal { get; set; }
    public IReadOnlyList<(string path, string error)> ErrorMessage => Parent?.ErrorMessage ?? ErrorMessageInternal;
    public void AddError(string path, string message)
    {
        (ErrorMessage as List<(string path, string error)>).Add((path, message));
        StopValidate = true;
    }
    public List<IValidateContext> Children { get; set; } = [];
    public IValidateContext Parent { get; set; }
    public CancellationToken CancellationToken { get; set; }
    public string ModelName { get; set; }
    private string _modelPath = string.Empty;
    public string ModelPath
    {
        get
        {
            if (_modelPath.IsNullOrEmpty())
            {
                //先获取前缀
                var prefix = Parent?.ModelPath;
                if (prefix.IsNullOrWhiteSpace())
                {
                    _modelPath = this.ModelName;
                }
                else
                {
                    if (this.ModelName == null)
                    {
                        _modelPath = prefix;
                    }
                    else if (this.ModelName.StartsWith('['))
                    {
                        _modelPath += prefix + this.ModelName;
                    }
                    else
                    {
                        _modelPath += $"{prefix}.{this.ModelName}";
                    }
                }
            }
            return _modelPath;
        }
    }
    public virtual object ModelValue { get; }

    public bool HasErrors() => ErrorMessage.IsNotNullOrEmpty();
}


internal class ValidateContext<T> : ValidateContext, IValidateContext<T>
{
    #region ctor
    /// <summary>
    /// 构造函数
    /// </summary>
    internal ValidateContext(T model, string modelName) : base(modelName, null, default, default)
    {
        Model = model;
    }

    /// <summary>
    /// 构造函数
    /// </summary>
    internal ValidateContext(T model, string modelName, IValidateContext parent, bool stopValidate) : base(modelName, parent, stopValidate, parent.CancellationToken)
    {
        Model = model;
    }

    /// <summary>
    /// 构造函数
    /// </summary>
    internal ValidateContext(T model, string modelName, IValidateContext parent) : base(modelName, parent, parent?.StopValidate ?? default, parent.CancellationToken)
    {
        Model = model;
    }

    /// <summary>
    /// 构造函数
    /// </summary>
    internal ValidateContext(T model, IValidateContext parent) : base(null, parent, parent?.StopValidate ?? default, parent.CancellationToken)
    {
        Model = model;
    }
    #endregion

    public T Model { get; }
    public override object ModelValue => Model;

    public IValidateContext<TProp> RuleFor<TProp>(Func<T, TProp> memberFunc, [CallerArgumentExpression(nameof(memberFunc))] string conditionExpression = null)
    {
        //获取prop的name
        var propName = VValid.AnalysisMember(conditionExpression);
        propName = VValid.AnalysisMemberName(typeof(T), propName);

        var propModel = memberFunc(Model);
        var newCtx = new ValidateContext<TProp>(propModel, propName, this);
        return newCtx;
    }

    public IValidateContext<T> RuleForIfNotNull<TProp>(Func<T, TProp> memberFunc, Action<IValidateContext<TProp>> act, [CallerArgumentExpression(nameof(memberFunc))] string conditionExpression = null)
    {
        var propModel = memberFunc(Model);
        if (propModel is not null)
        {
            //获取prop的name
            var propName = VValid.AnalysisMember(conditionExpression);
            propName = VValid.AnalysisMemberName(typeof(T), propName);
            var newCtx = new ValidateContext<TProp>(propModel, propName, this);
            act?.Invoke(newCtx);
        }
        return this;
    }

    public async Task RuleForIfNotNullAsync<TProp>(Func<T, TProp> memberFunc, Func<IValidateContext<TProp>, CancellationToken, Task> act, CancellationToken? cancellationToken = null, [CallerArgumentExpression("memberFunc")] string conditionExpression = null)
    {
        var propModel = memberFunc(Model);
        if (propModel is not null)
        {
            //获取prop的name
            var propName = VValid.AnalysisMember(conditionExpression);
            propName = VValid.AnalysisMemberName(typeof(T), propName);
            var newCtx = new ValidateContext<TProp>(propModel, propName, this);
            await act?.Invoke(newCtx, (cancellationToken ?? Parent?.CancellationToken) ?? default);
        }
    }

    public IValidateContext<T> WithDisplayName(string name)
    {
        this.ModelName = name;
        return this;
    }

    /// <summary>
    /// 如果已经检查失败了, 就通过抛异常的方式中断检查
    /// </summary>
    /// <remarks>
    /// 注意: 抛的异常 <seealso cref="ValidateException"/> 最终被外层方法如: <seealso cref="VValid.ValidMessage{T}(T, Action{IValidateContext{T}})"/> 捕获<br />
    /// 自定义代码中不要尝试捕获此异常
    /// </remarks>
    public IValidateContext<T> IfFailThenExit()
    {
        if (HasErrors()) throw new ValidateException();
        return this;
    }

    #region 分支到其他校验逻辑

    public IValidateContext<T> UseValidate(Action<IValidateContext<T>> action)
    {
        action?.Invoke(this);
        return this;
    }

    public async Task UseValidateAsync(Func<IValidateContext<T>, CancellationToken, Task> action, CancellationToken? cancellationToken = null)
    {
        await action?.Invoke(this, cancellationToken ?? Parent?.CancellationToken ?? default);
    }

    public IValidateContext<T> When(Func<T, bool> predicate, Action<IValidateContext<T>> action)
    {
        var b = predicate?.Invoke(Model);
        if (b == true) action?.Invoke(this);
        return this;
    }

    public IValidateContext<T> WhenNotNull(Action<IValidateContext<T>> action)
    {
        if (this.Model != null) action?.Invoke(this);
        return this;
    }

    public async Task WhenAsync(Func<T, CancellationToken, Task<bool>> predicate, Func<IValidateContext<T>, CancellationToken, Task> action, CancellationToken? cancellationToken = null)
    {
        var b = await predicate?.Invoke(Model, cancellationToken ?? Parent?.CancellationToken ?? default);
        if (b == true) action?.Invoke(this, cancellationToken ?? Parent?.CancellationToken ?? default);
    }

    public IValidateContext<T> MustReturnTrue(Func<T, bool> mustReturnTrue, string errorMessage = null)
    {
        var b = mustReturnTrue?.Invoke(Model);
        if (b == false)
        {
            AddError(ModelPath, errorMessage ?? $"'{ModelPath}' 校验失败!");
        }
        return this;
    }

    public async Task MustReturnTrueAsync(Func<T, CancellationToken, Task<bool>> mustReturnTrue, string errorMessage = null, CancellationToken? cancellationToken = null)
    {
        var b = await mustReturnTrue?.Invoke(Model, cancellationToken ?? Parent?.CancellationToken ?? default);
        if (b == false)
        {
            AddError(ModelPath, errorMessage ?? $"'{ModelPath}' 校验失败!");
        }
    }

    public IValidateContext<T> MustNotException(Action<T> mustNotException, string errorMessage = null)
    {
        try
        {
            mustNotException?.Invoke(Model);
        }
        catch (Exception ex)
        {
            if (ex is ValidateException) throw;
            AddError(ModelPath, errorMessage ?? $"'{ModelPath}' 校验失败!({ex.Message})");
        }
        return this;
    }

    public async Task MustNotExceptionAsync(Func<T, CancellationToken, Task> mustNotException, string errorMessage = null, CancellationToken? cancellationToken = null)
    {
        try
        {
            await mustNotException?.Invoke(Model, cancellationToken ?? Parent?.CancellationToken ?? default);
        }
        catch (Exception ex)
        {
            if (ex is ValidateException) throw;
            AddError(ModelPath, errorMessage ?? $"'{ModelPath}' 校验失败!({ex.Message})");
        }
    }
    #endregion
}
